home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume23 / trn / part05 < prev    next >
Encoding:
Text File  |  1991-08-22  |  64.1 KB  |  2,601 lines

  1. This is a new archive version of TRN at patchlevel 3.
  2. The original posting took up Volume23, issues 60 to 73, with
  3. various problems.  These files replace those issues.
  4.  
  5. #! /bin/sh
  6. # This is a shell archive.  Remove anything before this line, then feed it
  7. # into a shell via "sh file" or similar.  To overwrite existing files,
  8. # type "sh file -c".
  9. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  10. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  11. # Contents:  intrp.c mt-process.c
  12. # Wrapped by rsalz@litchi.bbn.com on Fri Aug 23 16:38:54 1991
  13. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  14. echo If this archive is complete, you will see the following message:
  15. echo '          "shar: End of archive 5 (of 14)."'
  16. if test -f 'intrp.c' -a "${1}" != "-c" ; then 
  17.   echo shar: Will not clobber existing file \"'intrp.c'\"
  18. else
  19.   echo shar: Extracting \"'intrp.c'\" \(26891 characters\)
  20.   sed "s/^X//" >'intrp.c' <<'END_OF_FILE'
  21. X/* $Header: intrp.c,v 4.3.3.3 91/01/16 02:43:42 davison Trn $
  22. X *
  23. X * $Log:    intrp.c,v $
  24. X * Revision 4.3.3.3  91/01/16  02:43:42  davison
  25. X * Integrated rn patches 48-54.
  26. X * 
  27. X * Revision 4.3.3.2  90/08/20  16:29:08  davison
  28. X * Added HOSTFILE handling.  Add OURDOMAIN if site has no '.'
  29. X * 
  30. X * Revision 4.3.3.1  90/07/21  20:20:13  davison
  31. X * Initial Trn Release
  32. X * 
  33. X * Revision 4.3.2.11  90/12/31  11:47:44  sob
  34. X * NEWSADMIN could not cancel articles because it was not getting set.
  35. X *
  36. X * Revision 4.3.2.10  90/12/31  00:02:55  sob
  37. X * Moved HIDDENET to remove unneeded cruft.
  38. X * 
  39. X * Revision 4.3.2.9  90/12/30  03:48:11  sob
  40. X * Changed "hidden" to "hiddennet" to be like nntp and bnews.
  41. X * Made it possible to cancel articles if hiddennet is defined.
  42. X * 
  43. X * Revision 4.3.2.8  90/11/22  13:52:27  sob
  44. X * Made changes to keep preprocessors from complaining.
  45. X * 
  46. X * Revision 4.3.2.7  90/11/05  23:59:33  sob
  47. X * moved the definition of tmpbuf such that it get defined before it is used.
  48. X * 
  49. X * Revision 4.3.2.6  90/11/03  18:52:31  sob
  50. X * Fixed bug in the definition of the nodename using the uname() system call.
  51. X * 
  52. X * Revision 4.3.2.5  90/10/01  01:31:18  sob
  53. X * Fixed problem with struct utsname reported by jrallen@devildog.att.com
  54. X * when rn is compiled on the Amdahl 5890 UTS 2.0 system.
  55. X * 
  56. X * Revision 4.3.2.4  90/04/23  00:31:20  sob
  57. X * Removed unneeded atoi call.
  58. X * 
  59. X * Revision 4.3.2.3  90/03/22  23:04:35  sob
  60. X * Fixes provided by Wayne Davison <drivax!davison>
  61. X * 
  62. X * Revision 4.3.2.2  90/03/17  17:03:12  sob
  63. X * Fixed determination of the news superuser's id. Fix provided by Chip
  64. X * Rosenthal <chip@chinacat.lonestar.org>.
  65. X * 
  66. X * Revision 4.3.2.1  89/12/17  02:54:55  sob
  67. X * Removed redundant include directive.
  68. X * 
  69. X * Revision 4.3.1.5  85/05/23  17:21:24  lwall
  70. X * Now allows 'r' and 'f' on null articles.
  71. X * 
  72. X * Revision 4.3.1.4  85/05/21  13:35:21  lwall
  73. X * Sped up "rn -c" by not doing unnecessary initialization.
  74. X * 
  75. X * Revision 4.3.1.3  85/05/17  10:37:11  lwall
  76. X * Fixed & substitution to capitalize last name too.
  77. X * 
  78. X * Revision 4.3.1.2  85/05/15  14:39:45  lwall
  79. X * Spelled gecos right.
  80. X * 
  81. X * Revision 4.3.1.1  85/05/10  11:33:51  lwall
  82. X * Branch for patches.
  83. X * 
  84. X * Revision 4.3  85/05/01  11:40:54  lwall
  85. X * Baseline for release with 4.3bsd.
  86. X * 
  87. X */
  88. X
  89. X#include "EXTERN.h"
  90. X#include "common.h"
  91. X#include "util.h"
  92. X#include "search.h"
  93. X#include "head.h"
  94. X#include "rn.h"
  95. X#include "artsrch.h"
  96. X#include "ng.h"
  97. X#include "respond.h"
  98. X#include "rcstuff.h"
  99. X#include "bits.h"
  100. X#include "artio.h"
  101. X#include "term.h"
  102. X#include "final.h"
  103. X#ifdef USETHREADS
  104. X#include "rthreads.h"
  105. X#endif
  106. X#include "INTERN.h"
  107. X#include "intrp.h"
  108. X
  109. Xchar orgname[] = ORGNAME;
  110. X
  111. X/* name of this site */
  112. X#ifndef HIDDENNET
  113. X#ifdef GETHOSTNAME
  114. X    char *hostname;
  115. X#   undef SITENAME
  116. X#   define SITENAME hostname
  117. X#else /* !GETHOSTNAME */
  118. X#   ifdef DOUNAME
  119. X#    include <sys/utsname.h>
  120. X    struct utsname utsn;
  121. X#    undef SITENAME
  122. X#    define SITENAME utsn.nodename
  123. X#   else /* !DOUNAME */
  124. X#    ifdef PHOSTNAME
  125. X        char *hostname;
  126. X#        undef SITENAME
  127. X#        define SITENAME hostname
  128. X#    else /* !PHOSTNAME */
  129. X#        ifdef WHOAMI
  130. X#        undef SITENAME
  131. X#        define SITENAME sysname
  132. X#        endif /* WHOAMI */
  133. X#    endif /* PHOSTNAME */
  134. X#   endif /* DOUNAME */
  135. X#endif /* GETHOSTNAME */
  136. X#endif /*HIDDENNET */
  137. X
  138. X#ifdef TILDENAME
  139. Xstatic char *tildename = Nullch;
  140. Xstatic char *tildedir = Nullch;
  141. X#endif
  142. X
  143. Xchar *realname INIT(Nullch);    /* real name of sender from /etc/passwd */
  144. X
  145. X#ifdef CONDSUB
  146. Xchar *skipinterp ANSI((char *,char *));
  147. X#endif
  148. X
  149. Xstatic void abort_interp ANSI((void));
  150. X
  151. Xvoid
  152. Xintrp_init(tcbuf)
  153. Xchar *tcbuf;
  154. X{
  155. X    char *getlogin();
  156. X
  157. X    spool = savestr(filexp(SPOOL));    /* usually /usr/spool/news */
  158. X    
  159. X    /* get environmental stuff */
  160. X
  161. X#ifdef NEWSADMIN
  162. X#ifdef GETPWENT
  163. X    {
  164. X    struct passwd *getpwnam();
  165. X    struct passwd *pwd = getpwnam(NEWSADMIN);
  166. X
  167. X    if (pwd != NULL)
  168. X        newsuid = pwd->pw_uid;
  169. X    }
  170. X#endif    /* GETPWENT */
  171. X#endif    /* NEWSADMIN */
  172. X    /* get home directory */
  173. X
  174. X    homedir = getenv("HOME");
  175. X    if (homedir == Nullch)
  176. X    homedir = getenv("LOGDIR");
  177. X
  178. X    dotdir = getval("DOTDIR",homedir);
  179. X
  180. X    /* get login name */
  181. X
  182. X    logname = getenv("USER");
  183. X    if (logname == Nullch)
  184. X    logname = getenv("LOGNAME");
  185. X#ifdef GETLOGIN
  186. X    if (logname == Nullch)
  187. X    logname = savestr(getlogin());
  188. X#endif
  189. X
  190. X#ifdef NEWSADMIN
  191. X    /* if this is the news admin than load his UID into newsuid */
  192. X
  193. X    if ( strEQ(logname,NEWSADMIN) )
  194. X    newsuid = getuid();
  195. X#endif
  196. X
  197. X    if (checkflag)            /* that getwd below takes ~1/3 sec. */
  198. X    return;                /* and we do not need it for -c */
  199. X    getwd(tcbuf);            /* find working directory name */
  200. X    origdir = savestr(tcbuf);        /* and remember it */
  201. X
  202. X    /* get the real name of the person (%N) */
  203. X    /* Must be done after logname is read in because BERKNAMES uses that */
  204. X
  205. X    strcpy(tcbuf,getrealname(getuid()));
  206. X    realname = savestr(tcbuf);
  207. X
  208. X    /* name of header file (%h) */
  209. X
  210. X    headname = savestr(filexp(HEADNAME));
  211. X
  212. X    /* name of this site (%H) */
  213. X
  214. X#ifndef HIDDENNET
  215. X#ifdef HOSTFILE
  216. X    if ((tmpfp = fopen(HOSTFILE,"r")) == NULL) {
  217. X    hostname = "unknown";
  218. X    printf("Warning: Couldn't open %s to determine hostname!\n", HOSTFILE); 
  219. X    } else {
  220. X    fgets(buf, sizeof(buf), tmpfp);
  221. X    buf[strlen(buf)-1] = 0;
  222. X    hostname = savestr(buf);
  223. X    fclose(tmpfp);
  224. X    }
  225. X#else
  226. X#ifdef GETHOSTNAME
  227. X    gethostname(buf,sizeof buf);
  228. X    hostname = savestr(buf);
  229. X#else
  230. X#ifdef DOUNAME
  231. X    /* get sysname */
  232. X    uname(&utsn);
  233. X#else
  234. X#ifdef PHOSTNAME
  235. X    {
  236. X    FILE *popen();
  237. X    FILE *pipefp = popen(PHOSTNAME,"r");
  238. X    
  239. X    if (pipefp == Nullfp) {
  240. X        printf("Can't find hostname\n");
  241. X        sig_catcher(0);
  242. X    }
  243. X    fgets(buf,sizeof buf,pipefp);
  244. X    buf[strlen(buf)-1] = '\0';    /* wipe out newline */
  245. X    hostname = savestr(buf);
  246. X    pclose(pipefp);
  247. X    }
  248. X#endif    /* PHOSTNAME */
  249. X#endif    /* DOUNAME */
  250. X#endif    /* GETHOSTNAME */
  251. X#endif    /* HOSTFILE */
  252. X    if (index(SITENAME,'.') == NULL) {
  253. X    sprintf(buf, "%s.%s", SITENAME, OURDOMAIN);
  254. X    sitename = savestr(buf);
  255. X    } else
  256. X    sitename = savestr(SITENAME);
  257. X#else
  258. X    sitename = savestr(OURDOMAIN);
  259. X#endif
  260. X}
  261. X
  262. X/* expand filename via %, ~, and $ interpretation */
  263. X/* returns pointer to static area */
  264. X/* Note that there is a 1-deep cache of ~name interpretation */
  265. X
  266. Xchar *
  267. Xfilexp(s)
  268. Xregister char *s;
  269. X{
  270. X    static char filename[CBUFLEN];
  271. X    char scrbuf[CBUFLEN];
  272. X    register char *d;
  273. X
  274. X#ifdef DEBUGGING
  275. X    if (debug & DEB_FILEXP)
  276. X    printf("< %s\n",s) FLUSH;
  277. X#endif
  278. X    interp(filename, (sizeof filename), s);            /* interpret any % escapes */
  279. X#ifdef DEBUGGING
  280. X    if (debug & DEB_FILEXP)
  281. X    printf("%% %s\n",filename) FLUSH;
  282. X#endif
  283. X    s = filename;
  284. X    if (*s == '~') {    /* does destination start with ~? */
  285. X    if (!*(++s) || *s == '/') {
  286. X        sprintf(scrbuf,"%s%s",homedir,s);
  287. X                /* swap $HOME for it */
  288. X#ifdef DEBUGGING
  289. X    if (debug & DEB_FILEXP)
  290. X    printf("~ %s\n",scrbuf) FLUSH;
  291. X#endif
  292. X        strcpy(filename,scrbuf);
  293. X    }
  294. X    else {
  295. X#ifdef TILDENAME
  296. X        for (d=scrbuf; isalnum(*s); s++,d++)
  297. X        *d = *s;
  298. X        *d = '\0';
  299. X        if (tildedir && strEQ(tildename,scrbuf)) {
  300. X        strcpy(scrbuf,tildedir);
  301. X        strcat(scrbuf, s);
  302. X        strcpy(filename, scrbuf);
  303. X#ifdef DEBUGGING
  304. X        if (debug & DEB_FILEXP)
  305. X            printf("r %s %s\n",tildename,tildedir) FLUSH;
  306. X#endif
  307. X        }
  308. X        else {
  309. X        if (tildename) {
  310. X            free(tildename);
  311. X            free(tildedir);
  312. X        }
  313. X        tildedir = Nullch;
  314. X        tildename = savestr(scrbuf);
  315. X#ifdef GETPWENT        /* getpwnam() is not the paragon of efficiency */
  316. X        {
  317. X            struct passwd *getpwnam();
  318. X            struct passwd *pwd = getpwnam(tildename);
  319. X
  320. X            sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
  321. X            tildedir = savestr(pwd->pw_dir);
  322. X            strcpy(filename,scrbuf);
  323. X#ifdef GETPWENT
  324. X            endpwent();
  325. X#endif
  326. X        }
  327. X#else            /* this will run faster, and is less D space */
  328. X        {    /* just be sure LOGDIRFIELD is correct */
  329. X            FILE *pfp = fopen("/etc/passwd","r");
  330. X            char tmpbuf[512];
  331. X            int i;
  332. X            
  333. X            if (pfp == Nullfp) {
  334. X            printf(cantopen,"passwd") FLUSH;
  335. X            sig_catcher(0);
  336. X            }
  337. X            while (fgets(tmpbuf,512,pfp) != Nullch) {
  338. X            d = cpytill(scrbuf,tmpbuf,':');
  339. X#ifdef DEBUGGING
  340. X            if (debug & DEB_FILEXP)
  341. X                printf("p %s\n",tmpbuf) FLUSH;
  342. X#endif
  343. X            if (strEQ(scrbuf,tildename)) {
  344. X                for (i=LOGDIRFIELD-2; i; i--) {
  345. X                if (d)
  346. X                    d = index(d+1,':');
  347. X                }
  348. X                if (d) {
  349. X                cpytill(scrbuf,d+1,':');
  350. X                tildedir = savestr(scrbuf);
  351. X                strcat(scrbuf,s);
  352. X                strcpy(filename,scrbuf);
  353. X                }
  354. X                break;
  355. X            }
  356. X            }
  357. X            fclose(pfp);
  358. X        }
  359. X#endif
  360. X        }
  361. X#else /* !TILDENAME */
  362. X#ifdef VERBOSE
  363. X        IF(verbose)
  364. X        fputs("~loginname not implemented.\n",stdout) FLUSH;
  365. X        ELSE
  366. X#endif
  367. X#ifdef TERSE
  368. X        fputs("~login not impl.\n",stdout) FLUSH;
  369. X#endif
  370. X#endif
  371. X    }
  372. X    }
  373. X    else if (*s == '$') {    /* starts with some env variable? */
  374. X    d = scrbuf;
  375. X    *d++ = '%';
  376. X    if (s[1] == '{')
  377. X        strcpy(d,s+2);
  378. X    else {
  379. X        *d++ = '{';
  380. X        for (s++; isalnum(*s); s++) *d++ = *s;
  381. X                /* skip over token */
  382. X        *d++ = '}';
  383. X        strcpy(d,s);
  384. X    }
  385. X#ifdef DEBUGGING
  386. X    if (debug & DEB_FILEXP)
  387. X        printf("$ %s\n",scrbuf) FLUSH;
  388. X#endif
  389. X    interp(filename, (sizeof filename), scrbuf);
  390. X                    /* this might do some extra '%'s but */
  391. X                    /* that is how the Mercedes Benz */
  392. X    }
  393. X#ifdef DEBUGGING
  394. X    if (debug & DEB_FILEXP)
  395. X    printf("> %s\n",filename) FLUSH;
  396. X#endif
  397. X    return filename;
  398. X}
  399. X
  400. X#ifdef CONDSUB
  401. X/* skip interpolations */
  402. X
  403. Xchar *
  404. Xskipinterp(pattern,stoppers)
  405. Xregister char *pattern;
  406. Xchar *stoppers;
  407. X{
  408. X
  409. X    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
  410. X#ifdef DEBUGGING
  411. X    if (debug & 8)
  412. X        printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern);
  413. X#endif
  414. X    if (*pattern == '%' && pattern[1]) {
  415. X        switch (*++pattern) {
  416. X        case '{':
  417. X        for (pattern++; *pattern && *pattern != '}'; pattern++)
  418. X            if (*pattern == '\\')
  419. X            pattern++;
  420. X        break;
  421. X        case '[':
  422. X        for (pattern++; *pattern && *pattern != ']'; pattern++)
  423. X            if (*pattern == '\\')
  424. X            pattern++;
  425. X        break;
  426. X#ifdef CONDSUB
  427. X        case '(': {
  428. X        pattern = skipinterp(pattern+1,"!=");
  429. X        if (!*pattern)
  430. X            goto getout;
  431. X        for (pattern++; *pattern && *pattern != '?'; pattern++)
  432. X            if (*pattern == '\\')
  433. X            pattern++;
  434. X        if (!*pattern)
  435. X            goto getout;
  436. X        pattern = skipinterp(pattern+1,":)");
  437. X        if (*pattern == ':')
  438. X            pattern = skipinterp(pattern+1,")");
  439. X        break;
  440. X        }
  441. X#endif
  442. X#ifdef BACKTICK
  443. X        case '`': {
  444. X        pattern = skipinterp(pattern+1,"`");
  445. X        break;
  446. X        }
  447. X#endif
  448. X#ifdef PROMPTTTY
  449. X        case '"':
  450. X        pattern = skipinterp(pattern+1,"\"");
  451. X        break;
  452. X#endif
  453. X        default:
  454. X        break;
  455. X        }
  456. X        pattern++;
  457. X    }
  458. X    else {
  459. X        if (*pattern == '^' && pattern[1])
  460. X        pattern += 2;
  461. X        else if (*pattern == '\\' && pattern[1])
  462. X        pattern += 2;
  463. X        else
  464. X        pattern++;
  465. X    }
  466. X    }
  467. Xgetout:
  468. X    return pattern;            /* where we left off */
  469. X}
  470. X#endif
  471. X
  472. X/* interpret interpolations */
  473. X
  474. Xchar *
  475. Xdointerp(dest,destsize,pattern,stoppers)
  476. Xregister char *dest;
  477. Xregister int destsize;
  478. Xregister char *pattern;
  479. Xchar *stoppers;
  480. X{
  481. X    char *subj_buf = Nullch;
  482. X    char *ngs_buf = Nullch;
  483. X    char *refs_buf = Nullch;
  484. X    char *artid_buf = Nullch;
  485. X    char *reply_buf = Nullch;
  486. X    char *from_buf = Nullch;
  487. X    char *path_buf = Nullch;
  488. X    char *follow_buf = Nullch;
  489. X    char *dist_buf = Nullch;
  490. X    char *line_buf = Nullch;
  491. X    register char *s, *h;
  492. X    register int i;
  493. X    char scrbuf[512];
  494. X    bool upper = FALSE;
  495. X    bool lastcomp = FALSE;
  496. X    int metabit = 0;
  497. X
  498. X    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
  499. X#ifdef DEBUGGING
  500. X    if (debug & 8)
  501. X        printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
  502. X#endif
  503. X    if (*pattern == '%' && pattern[1]) {
  504. X        upper = FALSE;
  505. X        lastcomp = FALSE;
  506. X        for (s=Nullch; !s; ) {
  507. X        switch (*++pattern) {
  508. X        case '^':
  509. X            upper = TRUE;
  510. X            break;
  511. X        case '_':
  512. X            lastcomp = TRUE;
  513. X            break;
  514. X        case '/':
  515. X#ifdef ARTSRCH
  516. X            s = scrbuf;
  517. X            if (!index("/?g",pattern[-2]))
  518. X            *s++ = '/';
  519. X            strcpy(s,lastpat);
  520. X            s += strlen(s);
  521. X            if (pattern[-2] != 'g') {
  522. X            if (index("/?",pattern[-2]))
  523. X                *s++ = pattern[-2];
  524. X            else
  525. X                *s++ = '/';
  526. X            if (art_howmuch == 1)
  527. X                *s++ = 'h';
  528. X            else if (art_howmuch == 2)
  529. X                *s++ = 'a';
  530. X            if (art_doread)
  531. X                *s++ = 'r';
  532. X            }
  533. X            *s = '\0';
  534. X            s = scrbuf;
  535. X#else
  536. X            s = nullstr;
  537. X#endif
  538. X            break;
  539. X        case '{':
  540. X            pattern = cpytill(scrbuf,pattern+1,'}');
  541. X            if (s = index(scrbuf,'-'))
  542. X            *s++ = '\0';
  543. X            else
  544. X            s = nullstr;
  545. X            s = getval(scrbuf,s);
  546. X            break;
  547. X        case '[':
  548. X            pattern = cpytill(scrbuf,pattern+1,']');
  549. X            i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
  550. X            if (line_buf)
  551. X            free(line_buf);
  552. X            s = line_buf = fetchlines(art,i);
  553. X            break;
  554. X#ifdef CONDSUB
  555. X        case '(': {
  556. X            COMPEX *oldbra_compex = bra_compex;
  557. X            COMPEX cond_compex;
  558. X            char rch;
  559. X            bool matched;
  560. X            
  561. X            init_compex(&cond_compex);
  562. X            pattern = dointerp(dest,destsize,pattern+1,"!=");
  563. X            rch = *pattern;
  564. X            if (rch == '!')
  565. X            pattern++;
  566. X            if (*pattern != '=')
  567. X            goto getout;
  568. X            pattern = cpytill(scrbuf,pattern+1,'?');
  569. X            if (!*pattern)
  570. X            goto getout;
  571. X            if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
  572. X            printf("%s: %s\n",scrbuf,s) FLUSH;
  573. X            pattern += strlen(pattern);
  574. X            goto getout;
  575. X            }
  576. X            matched = (execute(&cond_compex,dest) != Nullch);
  577. X            if (cond_compex.nbra)    /* were there brackets? */
  578. X            bra_compex = &cond_compex;
  579. X            if (matched==(rch == '=')) {
  580. X            pattern = dointerp(dest,destsize,pattern+1,":)");
  581. X            if (*pattern == ':')
  582. X                pattern = skipinterp(pattern+1,")");
  583. X            }
  584. X            else {
  585. X            pattern = skipinterp(pattern+1,":)");
  586. X            if (*pattern == ':')
  587. X                pattern++;
  588. X            pattern = dointerp(dest,destsize,pattern,")");
  589. X            }
  590. X            s = dest;
  591. X            bra_compex = oldbra_compex;
  592. X            free_compex(&cond_compex);
  593. X            break;
  594. X        }
  595. X#endif
  596. X#ifdef BACKTICK
  597. X        case '`': {
  598. X            FILE *pipefp, *popen();
  599. X
  600. X            pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
  601. X            pipefp = popen(scrbuf,"r");
  602. X            if (pipefp != Nullfp) {
  603. X            int len;
  604. X
  605. X            len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
  606. X                pipefp);
  607. X            scrbuf[len] = '\0';
  608. X            pclose(pipefp);
  609. X            }
  610. X            else {
  611. X            printf("\nCan't run %s\n",scrbuf);
  612. X            *scrbuf = '\0';
  613. X            }
  614. X            for (s=scrbuf; *s; s++) {
  615. X            if (*s == '\n') {
  616. X                if (s[1])
  617. X                *s = ' ';
  618. X                else
  619. X                *s = '\0';
  620. X            }
  621. X            }
  622. X            s = scrbuf;
  623. X            break;
  624. X        }
  625. X#endif
  626. X#ifdef PROMPTTTY
  627. X        case '"':
  628. X            pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
  629. X            fputs(scrbuf,stdout) FLUSH;
  630. X            resetty();
  631. X            gets(scrbuf);
  632. X            noecho();
  633. X            crmode();
  634. X            s = scrbuf;
  635. X            break;
  636. X#endif
  637. X        case '~':
  638. X            s = homedir;
  639. X            break;
  640. X        case '.':
  641. X            s = dotdir;
  642. X            break;
  643. X        case '$':
  644. X            s = scrbuf;
  645. X            sprintf(s,"%d",getpid());
  646. X            break;
  647. X        case '#':
  648. X            s = scrbuf;
  649. X            sprintf(s,"%d",perform_cnt);
  650. X            break;
  651. X        case '0': case '1': case '2': case '3': case '4':
  652. X        case '5': case '6': case '7': case '8': case '9':
  653. X#ifdef CONDSUB
  654. X            s = getbracket(bra_compex,*pattern - '0');
  655. X#else
  656. X            s = nullstr;
  657. X#endif
  658. X            break;
  659. X        case 'a':
  660. X            s = scrbuf;
  661. X            sprintf(s,"%ld",(long)art);
  662. X            break;
  663. X        case 'A':
  664. X#ifdef LINKART
  665. X            s = linkartname;    /* so Eunice people get right file */
  666. X#else
  667. X            s = scrbuf;
  668. X            sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
  669. X#endif
  670. X            break;
  671. X        case 'b':
  672. X            s = savedest;
  673. X            break;
  674. X        case 'B':
  675. X            s = scrbuf;
  676. X            sprintf(s,"%ld",(long)savefrom);
  677. X            break;
  678. X        case 'c':
  679. X            s = ngdir;
  680. X            break;
  681. X        case 'C':
  682. X            s = ngname;
  683. X            break;
  684. X        case 'd':
  685. X            s = scrbuf;
  686. X            sprintf(s,"%s/%s",spool,ngdir);
  687. X            break;
  688. X        case 'D':
  689. X            s = dist_buf = fetchlines(art,DIST_LINE);
  690. X            break;
  691. X        case 'e':
  692. X            s = extractprog;
  693. X            break;
  694. X#ifdef USETHREADS
  695. X        case 'E': {
  696. X            int sel, unseen;
  697. X
  698. X            sel = curr_p_art && (selected_roots[curr_p_art->root] & 1);
  699. X            unseen = (art <= lastart) && !was_read(art);
  700. X            sprintf(scrbuf,"%ld",(long)toread[ng]-selected_count
  701. X                        -unthreaded-(!sel && unseen));
  702. X            s = scrbuf;
  703. X            break;
  704. X        }
  705. X#endif
  706. X        case 'f':            /* from line */
  707. X#ifdef ASYNC_PARSE
  708. X            parse_maybe(art);
  709. X#endif
  710. X            if (htype[REPLY_LINE].ht_minpos >= 0) {
  711. X                        /* was there a reply line? */
  712. X            if (!(s=reply_buf))
  713. X                s = reply_buf = fetchlines(art,REPLY_LINE);
  714. X            }
  715. X            else if (!(s = from_buf))
  716. X            s = from_buf = fetchlines(art,FROM_LINE);
  717. X            break;
  718. X        case 'F':
  719. X#ifdef ASYNC_PARSE
  720. X            parse_maybe(art);
  721. X#endif
  722. X            if (htype[FOLLOW_LINE].ht_minpos >= 0)
  723. X                    /* is there a Followup-To line? */
  724. X            s = follow_buf = fetchlines(art,FOLLOW_LINE);
  725. X            else {
  726. X            int off;
  727. X        
  728. X            s = ngs_buf = fetchlines(art,NGS_LINE);
  729. X            if (h = instr(s,"net.general")) {
  730. X                off = h-s;
  731. X                strncpy(scrbuf,s,off+4);
  732. X                strcpy(scrbuf+off+4,"followup");
  733. X                safecpy(scrbuf+off+12,h+11,sizeof(scrbuf));
  734. X                s = scrbuf;
  735. X            }
  736. X            }
  737. X            break;
  738. X        case 'h':            /* header file name */
  739. X            s = headname;
  740. X            break;
  741. X        case 'H':            /* host name */
  742. X            s = sitename;
  743. X            break;
  744. X        case 'i':
  745. X            if (!(s=artid_buf))
  746. X            s = artid_buf = fetchlines(art,MESSID_LINE);
  747. X            if (*s && *s != '<') {
  748. X            sprintf(scrbuf,"<%s>",artid_buf);
  749. X            s = scrbuf;
  750. X            }
  751. X            break;
  752. X        case 'I':            /* ref article indicator */
  753. X            s = scrbuf;
  754. X            sprintf(scrbuf,"'%s'",indstr);
  755. X            break;
  756. X        case 'l':            /* rn library */
  757. X#ifdef NEWSADMIN
  758. X            s = newsadmin;
  759. X#else
  760. X            s = "???";
  761. X#endif
  762. X            break;
  763. X        case 'L':            /* login id */
  764. X            s = logname;
  765. X            break;
  766. X        case 'm':        /* current mode */
  767. X            s = scrbuf;
  768. X            *s = mode;
  769. X            s[1] = '\0';
  770. X            break;
  771. X        case 'M':
  772. X#ifdef DELAYMARK
  773. X            sprintf(scrbuf,"%ld",(long)dmcount);
  774. X            s = scrbuf;
  775. X#else
  776. X            s = nullstr;
  777. X#endif
  778. X            break;
  779. X        case 'n':            /* newsgroups */
  780. X            s = ngs_buf = fetchlines(art,NGS_LINE);
  781. X            break;
  782. X        case 'N':            /* full name */
  783. X            s = getval("NAME",realname);
  784. X            break;
  785. X        case 'o':            /* organization */
  786. X            s = getval("ORGANIZATION",orgname);
  787. X#ifdef ORGFILE
  788. X            if (*s == '/') {
  789. X            FILE *ofp = fopen(s,"r");
  790. X
  791. X            if (ofp) {
  792. X                fgets(scrbuf,sizeof scrbuf,ofp);
  793. X                fclose(ofp);
  794. X                s = scrbuf;
  795. X                s[strlen(s)-1] = '\0';
  796. X            }
  797. X            }
  798. X#endif
  799. X            break;
  800. X        case 'O':
  801. X            s = origdir;
  802. X            break;
  803. X        case 'p':
  804. X            s = cwd;
  805. X            break;
  806. X        case 'P':
  807. X            s = spool;
  808. X            break;
  809. X        case 'r':
  810. X#ifdef ASYNC_PARSE
  811. X            parse_maybe(art);
  812. X#endif
  813. X            if (htype[REFS_LINE].ht_minpos >= 0) {
  814. X            refs_buf = fetchlines(art,REFS_LINE);
  815. X            refscpy(scrbuf,(sizeof scrbuf),refs_buf);
  816. X            }
  817. X            else
  818. X            *scrbuf = '\0';
  819. X            s = rindex(scrbuf,'<');
  820. X            break;
  821. X        case 'R':
  822. X#ifdef ASYNC_PARSE
  823. X            parse_maybe(art);
  824. X#endif
  825. X            if (htype[REFS_LINE].ht_minpos >= 0) {
  826. X            refs_buf = fetchlines(art,REFS_LINE);
  827. X            refscpy(scrbuf,(sizeof scrbuf),refs_buf);
  828. X            /* no more than 3 prior references allowed,
  829. X            ** including the one concatenated below */
  830. X            if ((s = rindex(scrbuf,'<')) > scrbuf) {
  831. X                *s = '\0';
  832. X                h = rindex(scrbuf,'<');
  833. X                *s = '<';
  834. X                if (h > scrbuf)
  835. X                strcpy(scrbuf,h);
  836. X            }
  837. X            }
  838. X            else
  839. X            *scrbuf = '\0';
  840. X            if (!artid_buf)
  841. X            artid_buf = fetchlines(art,MESSID_LINE);
  842. X            if (artid_buf[0] == '<')
  843. X            safecat(scrbuf,artid_buf,sizeof(scrbuf));
  844. X            else if (artid_buf[0]) {
  845. X            char tmpbuf[64];
  846. X    
  847. X            sprintf(tmpbuf,"<%s>",artid_buf);
  848. X            safecat(scrbuf,tmpbuf,sizeof(scrbuf));
  849. X            }
  850. X            s = scrbuf;
  851. X            break;
  852. X        case 's':
  853. X            if (!(s=subj_buf))
  854. X            s = subj_buf = fetchsubj(art,TRUE,TRUE);
  855. X                        /* get subject handy */
  856. X            while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
  857. X                        /* skip extra Re: */
  858. X            s += 3;
  859. X            if (*s == ' ')
  860. X                s++;
  861. X            }
  862. X            if (h = instr(s,"- (nf"))
  863. X            *h = '\0';
  864. X            break;
  865. X        case 'S':
  866. X            if (!(s=subj_buf))
  867. X            s = subj_buf = fetchsubj(art,TRUE,TRUE);
  868. X                        /* get subject handy */
  869. X            if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
  870. X                        /* skip extra Re: */
  871. X            s += 3;
  872. X            if (*s == ' ')
  873. X                s++;
  874. X            }
  875. X            break;
  876. X        case 't':
  877. X        case 'T':
  878. X#ifdef ASYNC_PARSE
  879. X            parse_maybe(art);
  880. X#endif
  881. X            if (htype[REPLY_LINE].ht_minpos >= 0) {
  882. X                    /* was there a reply line? */
  883. X            if (!(s=reply_buf))
  884. X                s = reply_buf = fetchlines(art,REPLY_LINE);
  885. X            }
  886. X            else if (!(s = from_buf))
  887. X            s = from_buf = fetchlines(art,FROM_LINE);
  888. X            if (*pattern == 'T') {
  889. X            if (htype[PATH_LINE].ht_minpos >= 0) {
  890. X                    /* should we substitute path? */
  891. X                s = path_buf = fetchlines(art,PATH_LINE);
  892. X            }
  893. X            i = strlen(sitename);
  894. X            if (strnEQ(sitename,s,i) && s[i] == '!')
  895. X                s += i + 1;
  896. X            }
  897. X            if ((h=index(s,'(')) != Nullch)
  898. X                        /* strip garbage from end */
  899. X            *(h-1) = '\0';
  900. X            else if ((h=index(s,'<')) != Nullch) {
  901. X                        /* or perhaps from beginning */
  902. X            s = h+1;
  903. X            if ((h=index(s,'>')) != Nullch)
  904. X                *h = '\0';
  905. X            }
  906. X            break;
  907. X        case 'u':
  908. X            sprintf(scrbuf,"%ld",(long)toread[ng]);
  909. X            s = scrbuf;
  910. X            break;
  911. X        case 'U': {
  912. X            int unseen;
  913. X
  914. X            unseen = (art <= lastart) && !was_read(art);
  915. X#ifdef USETHREADS
  916. X            if (selected_root_cnt) {
  917. X            int sel;
  918. X
  919. X            sel = curr_p_art
  920. X                && (selected_roots[curr_p_art->root] & 1);
  921. X            sprintf(scrbuf,"%ld",
  922. X                (long)selected_count-(sel && unseen));
  923. X            }
  924. X            else
  925. X            sprintf(scrbuf,"%ld",(long)toread[ng]-unthreaded
  926. X                        -unseen);
  927. X#else
  928. X            sprintf(scrbuf,"%ld",(long)toread[ng]-unseen);
  929. X#endif
  930. X            s = scrbuf;
  931. X            break;
  932. X        }
  933. X        case 'x':            /* news library */
  934. X            s = lib;
  935. X            break;
  936. X        case 'X':            /* rn library */
  937. X            s = rnlib;
  938. X            break;
  939. X        case 'z':
  940. X#ifdef LINKART
  941. X            s = linkartname;    /* so Eunice people get right file */
  942. X#else
  943. X            s = scrbuf;
  944. X            sprintf(s,"%ld",(long)art);
  945. X#endif
  946. X            if (stat(s,&filestat) < 0)
  947. X            filestat.st_size = 0L;
  948. X            sprintf(scrbuf,"%5ld",(long)filestat.st_size);
  949. X            s = scrbuf;
  950. X            break;
  951. X#ifdef USETHREADS
  952. X        case 'Z':
  953. X            sprintf(scrbuf,"%ld",(long)selected_count);
  954. X            s = scrbuf;
  955. X            break;
  956. X#endif
  957. X        default:
  958. X            if (--destsize <= 0)
  959. X            abort_interp();
  960. X            *dest++ = *pattern | metabit;
  961. X            s = nullstr;
  962. X            break;
  963. X        }
  964. X        }
  965. X        if (!s)
  966. X        s = nullstr;
  967. X        pattern++;
  968. X        if (upper || lastcomp) {
  969. X        char *t;
  970. X
  971. X        if (s != scrbuf) {
  972. X            safecpy(scrbuf,s,(sizeof scrbuf));
  973. X            s = scrbuf;
  974. X        }
  975. X        if (upper || !(t=rindex(s,'/')))
  976. X            t = s;
  977. X        while (*t && !isalpha(*t))
  978. X            t++;
  979. X        if (islower(*t))
  980. X            *t = toupper(*t);
  981. X        }
  982. X        i = metabit;        /* maybe get into register */
  983. X        if (s == dest) {
  984. X        while (*dest) {
  985. X            if (--destsize <= 0)
  986. X            abort_interp();
  987. X            *dest++ |= i;
  988. X        }
  989. X        }
  990. X        else {
  991. X        while (*s) {
  992. X            if (--destsize <= 0)
  993. X            abort_interp();
  994. X            *dest++ = *s++ | i;
  995. X        }
  996. X        }
  997. X    }
  998. X    else {
  999. X        if (--destsize <= 0)
  1000. X        abort_interp();
  1001. X        if (*pattern == '^' && pattern[1]) {
  1002. X        ++pattern;            /* skip uparrow */
  1003. X        i = *pattern;        /* get char into a register */
  1004. X        if (i == '?')
  1005. X            *dest++ = '\177' | metabit;
  1006. X        else if (i == '(') {
  1007. X            metabit = 0200;
  1008. X            destsize++;
  1009. X        }
  1010. X        else if (i == ')') {
  1011. X            metabit = 0;
  1012. X            destsize++;
  1013. X        }
  1014. X        else
  1015. X            *dest++ = i & 037 | metabit;
  1016. X        pattern++;
  1017. X        }
  1018. X        else if (*pattern == '\\' && pattern[1]) {
  1019. X        ++pattern;            /* skip backslash */
  1020. X        i = *pattern;        /* get char into a register */
  1021. X    
  1022. X        /* this used to be a switch but the if may save space */
  1023. X        
  1024. X        if (i >= '0' && i <= '7') {
  1025. X            i = 1;
  1026. X            while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
  1027. X            i <<= 3;
  1028. X            i += *pattern++ - '0';
  1029. X            }
  1030. X            *dest++ = i & 0377 | metabit;
  1031. X            --pattern;
  1032. X        }
  1033. X        else if (i == 'b')
  1034. X            *dest++ = '\b' | metabit;
  1035. X        else if (i == 'f')
  1036. X            *dest++ = '\f' | metabit;
  1037. X        else if (i == 'n')
  1038. X            *dest++ = '\n' | metabit;
  1039. X        else if (i == 'r')
  1040. X            *dest++ = '\r' | metabit;
  1041. X        else if (i == 't')
  1042. X            *dest++ = '\t' | metabit;
  1043. X        else
  1044. X            *dest++ = i | metabit;
  1045. X        pattern++;
  1046. X        }
  1047. X        else
  1048. X        *dest++ = *pattern++ | metabit;
  1049. X    }
  1050. X    }
  1051. X    *dest = '\0';
  1052. Xgetout:
  1053. X    if (subj_buf != Nullch)    /* return any checked out storage */
  1054. X    free(subj_buf);
  1055. X    if (ngs_buf != Nullch)
  1056. X    free(ngs_buf);
  1057. X    if (refs_buf != Nullch)
  1058. X    free(refs_buf);
  1059. X    if (artid_buf != Nullch)
  1060. X    free(artid_buf);
  1061. X    if (reply_buf != Nullch)
  1062. X    free(reply_buf);
  1063. X    if (from_buf != Nullch)
  1064. X    free(from_buf);
  1065. X    if (path_buf != Nullch)
  1066. X    free(path_buf);
  1067. X    if (follow_buf != Nullch)
  1068. X    free(follow_buf);
  1069. X    if (dist_buf != Nullch)
  1070. X    free(dist_buf);
  1071. X    if (line_buf != Nullch)
  1072. X    free(line_buf);
  1073. X    return pattern;            /* where we left off */
  1074. X}
  1075. X
  1076. Xvoid
  1077. Xinterp(dest,destsize,pattern)
  1078. Xchar *dest;
  1079. Xint destsize;
  1080. Xchar *pattern;
  1081. X{
  1082. X    dointerp(dest,destsize,pattern,Nullch);
  1083. X#ifdef DEBUGGING
  1084. X    if (debug & DEB_FILEXP)
  1085. X    fputs(dest,stdout);
  1086. X#endif
  1087. X}
  1088. X
  1089. X/* copy a references line, normalizing as we go */
  1090. X
  1091. Xvoid
  1092. Xrefscpy(dest,destsize,src)
  1093. Xregister char *dest, *src;
  1094. Xregister int destsize;
  1095. X{
  1096. X    register char *dot, *at, *beg;
  1097. X    char tmpbuf[64];
  1098. X    
  1099. X    while (*src) {
  1100. X    if (*src != '<') {
  1101. X        if (--destsize <= 0)
  1102. X        break;
  1103. X        *dest++ = '<';
  1104. X        at = dot = Nullch;
  1105. X        beg = src;
  1106. X        while (*src && *src != ' ' && *src != ',') {
  1107. X        if (*src == '.')
  1108. X            dot = src;
  1109. X        else if (*src == '@')
  1110. X            at = src;
  1111. X        if (--destsize <= 0)
  1112. X            break;
  1113. X        *dest++ = *src++;
  1114. X        }
  1115. X        if (destsize <= 0)
  1116. X        break;
  1117. X        if (dot && !at) {
  1118. X        int len;
  1119. X
  1120. X        *dest = *dot++ = '\0';
  1121. X        sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
  1122. X        len = strlen(tmpbuf);
  1123. X        if (destsize > len) {
  1124. X            strcpy(dest,tmpbuf);
  1125. X            dest = dest + len;
  1126. X            destsize -= len;
  1127. X        }
  1128. X        }
  1129. X        if (--destsize <= 0)
  1130. X        break;
  1131. X        *dest++ = '>';
  1132. X    }
  1133. X    else {
  1134. X        while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
  1135. X        if (destsize <= 0)
  1136. X        break;
  1137. X    }
  1138. X    while (*src == ' ' || *src == ',') src++;
  1139. X    if (*src && --destsize > 0)
  1140. X        *dest++ = ' ';
  1141. X    }
  1142. X    *dest = '\0';
  1143. X} 
  1144. X
  1145. X/* get the person's real name from /etc/passwd */
  1146. X/* (string is overwritten, so it must be copied) */
  1147. X
  1148. Xchar *
  1149. Xgetrealname(uid)
  1150. Xint uid;
  1151. X{
  1152. X    char *s, *c;
  1153. X    char tmpbuf[512];
  1154. X
  1155. X#ifdef PASSNAMES
  1156. X#ifdef GETPWENT
  1157. X    struct passwd *pwd = getpwuid(uid);
  1158. X    
  1159. X    s = pwd->pw_gecos;
  1160. X#else
  1161. X    int i;
  1162. X
  1163. X    getpw(uid, tmpbuf);
  1164. X    for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
  1165. X    if (s)
  1166. X        s = index(s,':')+1;
  1167. X    }
  1168. X    if (!s)
  1169. X    return nullstr;
  1170. X    cpytill(tmpbuf,s,':');
  1171. X    s = tmpbuf;
  1172. X#endif
  1173. X#ifdef BERKNAMES
  1174. X#ifdef BERKJUNK
  1175. X    while (*s && !isalnum(*s) && *s != '&') s++;
  1176. X#endif
  1177. X    if ((c = index(s, ',')) != Nullch)
  1178. X    *c = '\0';
  1179. X    if ((c = index(s, ';')) != Nullch)
  1180. X    *c = '\0';
  1181. X    s = cpytill(buf,s,'&');
  1182. X    if (*s == '&') {            /* whoever thought this one up was */
  1183. X    c = buf + strlen(buf);        /* in the middle of the night */
  1184. X    strcat(c,logname);        /* before the morning after */
  1185. X    strcat(c,s+1);
  1186. X    if (islower(*c))
  1187. X        *c = toupper(*c);        /* gack and double gack */
  1188. X    }
  1189. X#else
  1190. X    if ((c = index(s, '(')) != Nullch)
  1191. X    *c = '\0';
  1192. X    if ((c = index(s, '-')) != Nullch)
  1193. X    s = c;
  1194. X    strcpy(buf,tmpbuf);
  1195. X#endif
  1196. X#ifdef GETPWENT
  1197. X    endpwent();
  1198. X#endif
  1199. X    return buf;                /* return something static */
  1200. X#else
  1201. X    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
  1202. X    fgets(buf,sizeof buf,tmpfp);
  1203. X    fclose(tmpfp);
  1204. X    buf[strlen(buf)-1] = '\0';
  1205. X    return buf;
  1206. X    }
  1207. X    return "PUT YOUR NAME HERE";
  1208. X#endif
  1209. X}
  1210. X
  1211. Xstatic void
  1212. Xabort_interp()
  1213. X{
  1214. X    fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
  1215. X    sig_catcher(0);
  1216. X}
  1217. X
  1218. X
  1219. END_OF_FILE
  1220.   if test 26891 -ne `wc -c <'intrp.c'`; then
  1221.     echo shar: \"'intrp.c'\" unpacked with wrong size!
  1222.   fi
  1223.   # end of 'intrp.c'
  1224. fi
  1225. if test -f 'mt-process.c' -a "${1}" != "-c" ; then 
  1226.   echo shar: Will not clobber existing file \"'mt-process.c'\"
  1227. else
  1228.   echo shar: Extracting \"'mt-process.c'\" \(34306 characters\)
  1229.   sed "s/^X//" >'mt-process.c' <<'END_OF_FILE'
  1230. X/* $Header: mt-process.c,v 4.3.3.3 91/01/18 19:13:20 davison Trn $
  1231. X**
  1232. X** $Log:    mt-process.c,v $
  1233. X** Revision 4.3.3.3  91/01/18  19:13:20  davison
  1234. X** Removed the code that tried to exclude certain message ids.  Added -s option
  1235. X** 
  1236. X** Revision 4.3.3.2  90/08/20  16:40:31  davison
  1237. X** Added check of caught_interrupt flag into main loops.
  1238. X** 
  1239. X** Revision 4.3.3.1  90/07/28  18:04:45  davison
  1240. X** Initial Trn Release
  1241. X** 
  1242. X*/
  1243. X
  1244. X#include "EXTERN.h"
  1245. X#include "common.h"
  1246. X#include "mthreads.h"
  1247. X#ifdef SERVER
  1248. X#include "server.h"
  1249. X#endif
  1250. X
  1251. X#include <time.h>
  1252. X#ifndef TZSET
  1253. X# include <sys/timeb.h>
  1254. X#endif
  1255. X
  1256. Xchar buff[1024];
  1257. X
  1258. Xchar references[1024];
  1259. X
  1260. Xchar subject_str[80];
  1261. Xbool found_Re;
  1262. X
  1263. Xchar author_str[20];
  1264. X
  1265. Xextern int log_verbosity, slow_down;
  1266. X
  1267. XDOMAIN *next_domain;
  1268. X
  1269. Xvoid insert_article(), expire(), trim_roots(), order_roots(), trim_authors();
  1270. Xvoid make_root(), use_root(), merge_roots(), set_root(), unlink_root();
  1271. Xvoid link_child(), unlink_child();
  1272. Xvoid free_article(), free_domain(), free_subject(), free_root(), free_author();
  1273. Xvoid get_subject_str(), get_author_str();
  1274. XARTICLE *get_article();
  1275. XSUBJECT *new_subject();
  1276. XAUTHOR *new_author();
  1277. X
  1278. X#ifdef TZSET
  1279. Xextern time_t tnow;
  1280. X#else
  1281. Xextern struct timeb ftnow;
  1282. X#endif
  1283. X
  1284. X#ifndef SERVER
  1285. Xstatic FILE *fp_article;
  1286. X#endif
  1287. X
  1288. X/* Given the upper/lower bounds of the articles in the current group, add all
  1289. X** the ones that we don't know about and remove all the ones that have expired.
  1290. X** The current directory must be the newgroup's spool directory.
  1291. X*/
  1292. Xvoid
  1293. Xprocess_articles( first_article, last_article )
  1294. XART_NUM first_article, last_article;
  1295. X{
  1296. X    register char *cp, *str;
  1297. X    register ARTICLE *article;
  1298. X    register ART_NUM i;
  1299. X    time_t date;
  1300. X    int len;
  1301. X#ifdef SERVER
  1302. X    bool orig_extra = extra_expire;
  1303. X#endif
  1304. X    extern int errno;
  1305. X    extern int sys_nerr;
  1306. X    extern char *sys_errlist[];
  1307. X
  1308. X    if( first_article > (i = total.last+1) ) {
  1309. X    i = first_article;
  1310. X    }
  1311. X    added_count = last_article - i + 1;
  1312. X    expired_count = 0;
  1313. X
  1314. X    for( ; i <= last_article; i++ ) {
  1315. X    if( caught_interrupt ) {
  1316. X        return;
  1317. X    }
  1318. X#ifdef SERVER
  1319. X    if( slow_down ) {
  1320. X        sleep( slow_down );
  1321. X    }
  1322. X    sprintf( buff, "HEAD %ld", (long)i );
  1323. X    put_server( buff );
  1324. X    if( get_server( buff, sizeof buff ) < 0 || *buff == CHAR_FATAL ) {
  1325. X        last_article = i - 1;
  1326. X        extra_expire = FALSE;
  1327. X        break;
  1328. X    }
  1329. X    if( *buff != CHAR_OK ) {
  1330. X        added_count--;
  1331. X        continue;
  1332. X    }
  1333. X#else
  1334. X    /* Open article in current directory. */
  1335. X    sprintf( buff, "%ld", (long)i );
  1336. X    /* Set errno for purely paranoid reasons */
  1337. X    errno = 0;
  1338. X    if( (fp_article = fopen( buff, "r" )) == Nullfp ) {
  1339. X        /* Missing files are ok -- they've just been expired or canceled */
  1340. X        if( errno != 0 && errno != ENOENT ) {
  1341. X        if( errno < 0 || errno > sys_nerr ) {
  1342. X            log_error( "Can't open `%s': Error %d.\n", buff, errno );
  1343. X        } else {
  1344. X            log_error( "Can't open `%s': %s.\n", buff,
  1345. X              sys_errlist[errno] );
  1346. X        }
  1347. X        }
  1348. X        added_count--;
  1349. X        continue;
  1350. X    }
  1351. X#endif
  1352. X
  1353. X    article = Nullart;
  1354. X    *references = '\0';
  1355. X    *author_str = '\0';
  1356. X    *subject_str = '\0';
  1357. X    found_Re = 0;
  1358. X    date = 0;
  1359. X
  1360. X#ifdef SERVER
  1361. X    while( get_server( cp = buff, sizeof buff ) == 0 ) {
  1362. X      process_line:
  1363. X        if( *cp == '.' ) {
  1364. X        break;
  1365. X        }
  1366. X#else
  1367. X    while( (cp = fgets( buff, sizeof buff, fp_article )) != Nullch ) {
  1368. X      process_line:
  1369. X        if( *cp == '\n' ) {        /* check for end of header */
  1370. X        break;            /* break out when found */
  1371. X        }
  1372. X#endif
  1373. X        if( (unsigned char)*cp <= ' ' ) {     /* skip continuation lines */
  1374. X        continue;        /* (except references -- see below) */
  1375. X        }
  1376. X        if( (str = index( cp, ':' )) == Nullch ) {
  1377. X        break;            /* end of header if no colon found */
  1378. X        }
  1379. X        if( (len = str - cp) > 10 ) {
  1380. X        continue;        /* skip keywords > 10 chars */
  1381. X        }
  1382. X#ifndef SERVER
  1383. X        cp[strlen(cp)-1] = '\0';    /* remove newline */
  1384. X#endif
  1385. X        while( cp < str ) {        /* lower-case the keyword */
  1386. X        if( (unsigned char)*cp <= ' ' ) { /* stop at any whitespace */
  1387. X            break;
  1388. X        }
  1389. X        if( isupper(*cp) ) {
  1390. X            *cp = tolower(*cp);
  1391. X        }
  1392. X        cp++;
  1393. X        }
  1394. X        *cp = '\0';
  1395. X        cp = buff;
  1396. X        if( len == 4 && strEQ( cp, "date" ) ) {
  1397. X#ifdef TZSET
  1398. X            date = getdate( str + 1, tnow, timezone );
  1399. X#else
  1400. X        date = getdate( str + 1, ftnow.time, (long) ftnow.timezone );
  1401. X#endif
  1402. X        } else
  1403. X        if( len == 4 && strEQ( cp, "from" ) ) {
  1404. X        get_author_str( str + 1 );
  1405. X        } else
  1406. X        if( len == 7 && strEQ( cp, "subject" ) ) {
  1407. X        get_subject_str( str + 1 );
  1408. X        } else
  1409. X        if( len == 10 && strEQ( cp, "message-id" ) ) {
  1410. X        if( !article ) {
  1411. X            article = get_article( str + 1 );
  1412. X        } else {
  1413. X            if( log_verbosity ) {
  1414. X            log_error( "Found multiple Message-IDs! [%ld].\n",
  1415. X                (long)i );
  1416. X            }
  1417. X        }
  1418. X        } else
  1419. X        if( len == 10 && strEQ( cp, "references" ) ) {
  1420. X        /* include preceding space in saved reference */
  1421. X        len = strlen( str + 1 );
  1422. X        bcopy( str + 1, references, len + 1 );
  1423. X        str = references + len;
  1424. X        /* check for continuation lines */
  1425. X#ifdef SERVER
  1426. X        while( get_server( cp = buff, sizeof buff ) == 0 ) {
  1427. X#else
  1428. X        while( (cp = fgets( buff, sizeof buff, fp_article )) != Nullch ) {
  1429. X#endif
  1430. X            if( *cp != ' ' && *cp != '\t' ) {
  1431. X            goto process_line;
  1432. X            }
  1433. X            while( *++cp == ' ' || *cp == '\t' ) {
  1434. X            ;
  1435. X            }
  1436. X            *--cp = ' ';
  1437. X            /* If the references are too long, shift them over to
  1438. X            ** always save the most recent ones.
  1439. X            */
  1440. X            if( (len += strlen( cp )) > 1023 ) {
  1441. X            strcpy( buff, buff + len - 1023 );
  1442. X            str -= len - 1023;
  1443. X            len = 1023;
  1444. X            }
  1445. X            strcpy( str, cp );
  1446. X        }/* while */
  1447. X        break;
  1448. X        }/* if */
  1449. X    }/* while */
  1450. X    if( article ) {
  1451. X        insert_article( article, date, i );
  1452. X    } else {
  1453. X        if( log_verbosity ) {
  1454. X        log_error( "Message-ID line missing! [%ld].\n", (long)i );
  1455. X        }
  1456. X    }
  1457. X#ifndef SERVER
  1458. X    fclose( fp_article );
  1459. X#endif
  1460. X    }
  1461. X
  1462. X    if( extra_expire || first_article > total.first ) {
  1463. X    expire( first_article );
  1464. X    }
  1465. X    if( caught_interrupt ) {
  1466. X    return;
  1467. X    }
  1468. X    trim_roots();
  1469. X    order_roots();
  1470. X    trim_authors();
  1471. X
  1472. X    total.first = first_article;
  1473. X    total.last = last_article;
  1474. X#ifdef SERVER
  1475. X    extra_expire = orig_extra;
  1476. X#endif
  1477. X}
  1478. X
  1479. X/* Search all articles for numbers less than new_first.  Traverse the list
  1480. X** using the domain links so we don't have to deal with the tree structure.
  1481. X** If extra_expire is true, stat() all valid articles to make sure they are
  1482. X** really there and expire them if they're not.
  1483. X*/
  1484. Xvoid
  1485. Xexpire( new_first )
  1486. XART_NUM new_first;
  1487. X{
  1488. X    register DOMAIN *domain;
  1489. X    register ARTICLE *article, *next_art, *hold;
  1490. X
  1491. X    for( domain = &unk_domain; domain; domain = next_domain ) {
  1492. X    next_domain = domain->link;
  1493. X    for( article = domain->ids; article; article = next_art ) {
  1494. X        if( caught_interrupt ) {
  1495. X        return;
  1496. X        }
  1497. X        next_art = article->id_link;
  1498. X        if( !article->subject || (article->flags & NEW_ARTICLE) ) {
  1499. X        continue;
  1500. X        }
  1501. X        if( extra_expire && article->num >= new_first ) {
  1502. X#ifdef SERVER
  1503. X        sprintf( buff, "STAT %ld", (long)article->num );
  1504. X        put_server( buff );
  1505. X        if( get_server( buff, sizeof buff ) == 0 && *buff == CHAR_OK ) {
  1506. X            continue;
  1507. X        }
  1508. X#else
  1509. X        sprintf( buff, "%ld", (long)article->num );
  1510. X        if( !stat( buff, &filestat ) || errno != ENOENT ) {
  1511. X            continue;
  1512. X        }
  1513. X#endif
  1514. X        }
  1515. X        if( extra_expire || article->num < new_first ) {
  1516. X        article->subject->count--;
  1517. X        article->subject = 0;
  1518. X        article->author->count--;
  1519. X        article->author = 0;
  1520. X        /* Free expired article if it has no children.  Then check
  1521. X        ** if the parent(s) are also fake and can be freed.  We'll
  1522. X        ** free any empty roots later.
  1523. X        */
  1524. X        while( !article->children ) {
  1525. X            hold = article->parent;
  1526. X            unlink_child( article );
  1527. X            free_article( article );
  1528. X            if( hold && !hold->subject ) {
  1529. X            if( (article = hold) == next_art ) {
  1530. X                next_art = next_art->id_link;
  1531. X            }
  1532. X            } else {
  1533. X            break;
  1534. X            }
  1535. X        }
  1536. X        expired_count++;
  1537. X        }/* if */
  1538. X    }/* for */
  1539. X    }/* for */
  1540. X    next_domain = Null(DOMAIN*);
  1541. X}
  1542. X
  1543. X/* Trim the article chains down so that we don't have more than one faked
  1544. X** article between the root any real ones.
  1545. X*/
  1546. Xvoid
  1547. Xtrim_roots()
  1548. X{
  1549. X    register ROOT *root, *last_root;
  1550. X    register ARTICLE *article, *next;
  1551. X    register SUBJECT *subject, *last_subj;
  1552. X    register int found;
  1553. X
  1554. X#ifndef lint
  1555. X    last_root = (ROOT *)&root_root;
  1556. X#else
  1557. X    last_root = Null(ROOT*);
  1558. X#endif
  1559. X    for( root = root_root; root; root = last_root->link ) {
  1560. X    for( article = root->articles; article; article = article->siblings ) {
  1561. X        /* If an article has no subject, it is a "fake" reference node.
  1562. X        ** If all of its immediate children are also fakes, delete it
  1563. X        ** and graduate the children to the root.  If everyone is fake,
  1564. X        ** the chain dies.
  1565. X        */
  1566. X        while( !article->subject ) {
  1567. X        found = 0;
  1568. X        for( next = article->children; next; next = next->siblings ) {
  1569. X            if( next->subject ) {
  1570. X            found = 1;
  1571. X            break;
  1572. X            }
  1573. X        }
  1574. X        if( !found ) {
  1575. X            /* Remove this faked article and move all its children
  1576. X            ** up to the root.
  1577. X            */
  1578. X            next = article->children;
  1579. X            unlink_child( article );
  1580. X            free_article( article );
  1581. X            for( article = next; article; article = next ) {
  1582. X            next = article->siblings;
  1583. X            article->parent = Nullart;
  1584. X            link_child( article );
  1585. X            }
  1586. X            article = root->articles;    /* start this root over */
  1587. X        } else {
  1588. X            break;            /* else, on to next article */
  1589. X        }
  1590. X        }
  1591. X    }
  1592. X    /* Free all unused subject strings.  Begin by trying to find a
  1593. X    ** subject for the root's pointer.
  1594. X    */
  1595. X    for( subject = root->subjects; subject && !subject->count; subject = root->subjects ) {
  1596. X        root->subjects = subject->link;
  1597. X        free_subject( subject );
  1598. X        root->subject_cnt--;
  1599. X    }
  1600. X    /* Then free up any unsed intermediate subjects.
  1601. X    */
  1602. X    if( (last_subj = subject) != Null(SUBJECT*) ) {
  1603. X        while( (subject = subject->link) != Null(SUBJECT*) ) {
  1604. X        if( !subject->count ) {
  1605. X            last_subj->link = subject->link;
  1606. X            free_subject( subject );
  1607. X            root->subject_cnt--;
  1608. X            subject = last_subj;
  1609. X        } else {
  1610. X            last_subj = subject;
  1611. X        }
  1612. X        }
  1613. X    }
  1614. X    /* Now, free all roots without articles.  Flag unexpeced errors.
  1615. X    */
  1616. X    if( !root->articles ) {
  1617. X        if( root->subjects ) {
  1618. X        log_error( "** Empty root still had subjects remaining! **\n" );
  1619. X        }
  1620. X        last_root->link = root->link;
  1621. X        free_root( root );
  1622. X    } else {
  1623. X        last_root = root;
  1624. X    }
  1625. X    }
  1626. X}
  1627. X
  1628. X/* Descend the author list, find any author names that aren't used
  1629. X** anymore and free them.
  1630. X*/
  1631. Xvoid
  1632. Xtrim_authors()
  1633. X{
  1634. X    register AUTHOR *author, *last_author;
  1635. X
  1636. X#ifndef lint
  1637. X    last_author = (AUTHOR *)&author_root;
  1638. X#else
  1639. X    last_author = Null(AUTHOR*);
  1640. X#endif
  1641. X    for( author = author_root; author; author = last_author->link ) {
  1642. X    if( !author->count ) {
  1643. X        last_author->link = author->link;
  1644. X        free_author( author );
  1645. X    } else {
  1646. X        last_author = author;
  1647. X    }
  1648. X    }
  1649. X}
  1650. X
  1651. X/* Reorder the roots to place the oldest ones first (age determined by
  1652. X** date of oldest article).
  1653. X*/
  1654. Xvoid
  1655. Xorder_roots()
  1656. X{
  1657. X    register ROOT *root, *next, *search;
  1658. X
  1659. X    /* If we don't have at least two roots, we're done! */
  1660. X    if( !(root = root_root) || !(next = root->link) ) {
  1661. X    return;                        /* RETURN */
  1662. X    }
  1663. X    /* Break the old list off after the first root, and then start
  1664. X    ** inserting the roots into the list by date.
  1665. X    */
  1666. X    root->link = Null(ROOT*);
  1667. X    while( (root = next) != Null(ROOT*) ) {
  1668. X    next = next->link;
  1669. X    if( (search = root_root)->articles->date >= root->articles->date ) {
  1670. X        root->link = root_root;
  1671. X        root_root = root;
  1672. X    } else {
  1673. X        while( search->link
  1674. X         && search->link->articles->date < root->articles->date ) {
  1675. X        search = search->link;
  1676. X        }
  1677. X        root->link = search->link;
  1678. X        search->link = root;
  1679. X    }
  1680. X    }
  1681. X}
  1682. X
  1683. X#define EQ(x,y) ((isupper(x) ? tolower(x) : (x)) == (y))
  1684. X
  1685. X/* Parse the subject into 72 characters or less.  Remove any "Re[:^]"s from
  1686. X** the front (noting that it's there), and any "(was: old)" stuff from
  1687. X** the end.  Then, compact multiple whitespace characters into one space,
  1688. X** trimming leading/trailing whitespace.  If it's still too long, unmercifully
  1689. X** cut it off.  We don't bother with subject continuation lines either.
  1690. X*/
  1691. Xvoid
  1692. Xget_subject_str( str )
  1693. Xregister char *str;
  1694. X{
  1695. X    register char *cp;
  1696. X    register int len;
  1697. X
  1698. X    while( *str && (unsigned char)*str <= ' ' ) {
  1699. X    str++;
  1700. X    }
  1701. X    if( !*str ) {
  1702. X    bcopy( "<None>", subject_str, 7 );
  1703. X    return;                        /* RETURN */
  1704. X    }
  1705. X    cp = str;
  1706. X    while( EQ( cp[0], 'r' ) && EQ( cp[1], 'e' ) ) {    /* check for Re: */
  1707. X    cp += 2;
  1708. X    if( *cp == '^' ) {                /* allow Re^2: */
  1709. X        while( *++cp <= '9' && *cp >= '0' ) {
  1710. X        ;
  1711. X        }
  1712. X    }
  1713. X    if( *cp != ':' ) {
  1714. X        break;
  1715. X    }
  1716. X    while( *++cp == ' ' ) {
  1717. X        ;
  1718. X    }
  1719. X    found_Re = 1;
  1720. X    str = cp;
  1721. X    }
  1722. X    /* Remove "(was Re: oldsubject)", because we already know the old subjects.
  1723. X    ** Also match "(Re: oldsubject)".  Allow possible spaces after the ('s.
  1724. X    */
  1725. X    for( cp = str; (cp = index( cp+1, '(' )) != Nullch; ) {
  1726. X    while( *++cp == ' ' ) {
  1727. X        ;
  1728. X    }
  1729. X    if( EQ( cp[0], 'w' ) && EQ( cp[1], 'a' ) && EQ( cp[2], 's' )
  1730. X     && (cp[3] == ':' || cp[3] == ' ') )
  1731. X    {
  1732. X        *--cp = '\0';
  1733. X        break;
  1734. X    }
  1735. X    if( EQ( cp[0], 'r' ) && EQ( cp[1], 'e' )
  1736. X     && ((cp[2]==':' && cp[3]==' ') || (cp[2]=='^' && cp[4]==':')) ) {
  1737. X        *--cp = '\0';
  1738. X        break;
  1739. X    }
  1740. X    }
  1741. X    /* Copy subject to a temporary string, compacting multiple spaces/tabs */
  1742. X    for( len = 0, cp = subject_str; len < 72 && *str; len++ ) {
  1743. X    if( (unsigned char)*str <= ' ' ) {
  1744. X        while( *++str && (unsigned char)*str <= ' ' ) {
  1745. X        ;
  1746. X        }
  1747. X        *cp++ = ' ';
  1748. X    } else {
  1749. X        *cp++ = *str++;
  1750. X    }
  1751. X    }
  1752. X    if( cp[-1] == ' ' ) {
  1753. X    cp--;
  1754. X    }
  1755. X    *cp = '\0';
  1756. X}
  1757. X
  1758. X/* Try to fit the author name in 16 bytes.  Use the comment portion in
  1759. X** parenthesis if present.  Cut off non-commented names at the '@' or '%'.
  1760. X** Then, put as many characters as we can into the 16 bytes, packing multiple
  1761. X** whitespace characters into a single space.
  1762. X** We should really implement a nice name shortening algorithm, or simply
  1763. X** grab the name packing code from nn.
  1764. X*/
  1765. Xvoid
  1766. Xget_author_str( str )
  1767. Xchar *str;
  1768. X{
  1769. X    register char *cp, *cp2;
  1770. X
  1771. X    if( (cp = index( str, '(' )) != Nullch ) {
  1772. X    str = cp+1;
  1773. X    if( (cp = rindex( str, ')' )) != Nullch ) {
  1774. X        *cp = '\0';
  1775. X    }
  1776. X    } else {
  1777. X    if( (cp = index( str, '@' )) != Nullch ) {
  1778. X        *cp = '\0';
  1779. X    }
  1780. X    if( (cp = index( str, '%' )) != Nullch ) {
  1781. X        *cp = '\0';
  1782. X    }
  1783. X    }
  1784. X    for( cp = str, cp2 = author_str; *cp && cp2-author_str < 16; ) {
  1785. X    /* Pack white space and turn ctrl-chars into spaces. */
  1786. X    if( *cp <= ' ' ) {
  1787. X        while( *++cp && *cp <= ' ' ) {
  1788. X        ;
  1789. X        }
  1790. X        if( cp2 != author_str ) {
  1791. X        *cp2++ = ' ';
  1792. X        }
  1793. X    } else {
  1794. X        *cp2++ = *cp++;
  1795. X    }
  1796. X    }
  1797. X    *cp2 = '\0';
  1798. X}
  1799. X
  1800. X/* Take a message-id and see if we already know about it.  If so, return it.
  1801. X** If not, create it.  We separate the id into its id@domain parts, and
  1802. X** link all the unique ids to one copy of the domain portion.  This saves
  1803. X** a bit of space.
  1804. X*/
  1805. XARTICLE *
  1806. Xget_article( msg_id )
  1807. Xchar *msg_id;
  1808. X{
  1809. X    register DOMAIN *domain;
  1810. X    register ARTICLE *article;
  1811. X    register char *cp, *after_at;
  1812. X
  1813. X    /* Take message id, break it up into <id@domain>, and try to match it.
  1814. X    */
  1815. X    while( *msg_id == ' ' ) {
  1816. X    msg_id++;
  1817. X    }
  1818. X    cp = msg_id + strlen( msg_id ) - 1;
  1819. X    if( msg_id >= cp ) {
  1820. X    if( log_verbosity ) {
  1821. X        log_error( "Message-ID is empty!\n" );
  1822. X    }
  1823. X    return Nullart;
  1824. X    }
  1825. X    if( *msg_id++ != '<' ) {
  1826. X    if( log_verbosity ) {
  1827. X        log_error( "Message-ID doesn't start with '<'.\n" );
  1828. X    }
  1829. X    msg_id--;
  1830. X    }
  1831. X    if( *cp != '>' ) {
  1832. X    if( log_verbosity ) {
  1833. X        log_error( "Message-ID doesn't end with '>'.\n" );
  1834. X    }
  1835. X    cp++;
  1836. X    }
  1837. X    *cp = '\0';
  1838. X    if( msg_id == cp ) {
  1839. X    if( log_verbosity ) {
  1840. X        log_error( "Message-ID is null!\n" );
  1841. X    }
  1842. X    return Nullart;
  1843. X    }
  1844. X
  1845. X    if( (after_at = index( msg_id, '@' )) == Nullch ) {
  1846. X    domain = &unk_domain;
  1847. X    } else {
  1848. X    *after_at++ = '\0';
  1849. X    for( cp = after_at; *cp; cp++ ) {
  1850. X        if( isupper(*cp) ) {
  1851. X        *cp = tolower(*cp);        /* lower-case domain portion */
  1852. X        }
  1853. X    }
  1854. X    *cp = '\0';
  1855. X    /* Try to find domain name in database. */
  1856. X    for( domain = unk_domain.link; domain; domain = domain->link ) {
  1857. X        if( strEQ( domain->name, after_at ) ) {
  1858. X        break;
  1859. X        }
  1860. X    }
  1861. X    if( !domain ) {        /* if domain doesn't exist, create it */
  1862. X      register int len = cp - after_at + 1;
  1863. X        domain = (DOMAIN *)safemalloc( sizeof (DOMAIN) );
  1864. X        total.domain++;
  1865. X        domain->name = safemalloc( len );
  1866. X        total.string2 += len;
  1867. X        bcopy( after_at, domain->name, len );
  1868. X        domain->ids = Nullart;
  1869. X        domain->link = unk_domain.link;
  1870. X        unk_domain.link = domain;
  1871. X    }
  1872. X    }
  1873. X    /* Try to find id in this domain. */
  1874. X    for( article = domain->ids; article; article = article->id_link ) {
  1875. X    if( strEQ( article->id, msg_id ) ) {
  1876. X        break;
  1877. X    }
  1878. X    }
  1879. X    if( !article ) {        /* If it doesn't exist, create an article */
  1880. X      register int len = strlen( msg_id ) + 1;
  1881. X    article = (ARTICLE *)safemalloc( sizeof (ARTICLE) );
  1882. X    bzero( article, sizeof (ARTICLE) );
  1883. X    total.article++;
  1884. X    article->num = 0;
  1885. X    article->id = safemalloc( len );
  1886. X    total.string2 += len;
  1887. X    bcopy( msg_id, article->id, len );
  1888. X    article->domain = domain;
  1889. X    article->id_link = domain->ids;
  1890. X    domain->ids = article;
  1891. X    }
  1892. X    return article;
  1893. X}
  1894. X
  1895. X/* Take all the data we've accumulated about the article and shove it into
  1896. X** the article tree at the best place we can possibly imagine.
  1897. X*/
  1898. Xvoid
  1899. Xinsert_article( article, date, num )
  1900. XARTICLE *article;
  1901. Xtime_t date;
  1902. XART_NUM num;
  1903. X{
  1904. X    register ARTICLE *node, *last;
  1905. X    register char *cp, *end;
  1906. X    int len;
  1907. X
  1908. X    if( article->subject ) {
  1909. X    if( log_verbosity ) {
  1910. X        log_error( "We've already seen article #%ld (%s@%s)\n",
  1911. X        (long)num, article->id, article->domain->name );
  1912. X    }
  1913. X    return;                        /* RETURN */
  1914. X    }
  1915. X    article->date = date;
  1916. X    article->num = num;
  1917. X    article->flags = NEW_ARTICLE;
  1918. X
  1919. X    if( !*references && found_Re ) {
  1920. X    if( log_verbosity > 1 ) {
  1921. X        log_error( "Missing reference line!  [%ld]\n", (long)num );
  1922. X    }
  1923. X    }
  1924. X    /* If the article has a non-zero root, it is already in a thread somewhere.
  1925. X    ** Unlink it to try to put it in the best possible spot.
  1926. X    */
  1927. X    if( article->root ) {
  1928. X    /* Check for a real or shared-fake parent.  Articles that have never
  1929. X    ** existed have a num of 0.  Expired articles that remain as references
  1930. X    ** have a valid num.  (Valid date too, but no subject.)
  1931. X    */
  1932. X    for( node = article->parent;
  1933. X         node && !node->num && node->child_cnt == 1;
  1934. X         node = node->parent )
  1935. X    {
  1936. X        ;
  1937. X    }
  1938. X    unlink_child( article );
  1939. X    if( node ) {            /* do we have decent parents? */
  1940. X        /* Yes: assume that our references are ok, and just reorder us
  1941. X        ** with our siblings by date.
  1942. X        */
  1943. X        link_child( article );
  1944. X        use_root( article, article->root );
  1945. X        /* Freshen the date in any faked parent articles. */
  1946. X        for( node = article->parent;
  1947. X         node && !node->num && date < node->date;
  1948. X         node = node->parent )
  1949. X        {
  1950. X        node->date = date;
  1951. X        unlink_child( node );
  1952. X        link_child( node );
  1953. X        }
  1954. X        return;                    /* RETURN */
  1955. X    }
  1956. X    /* We'll assume that this article has as good or better references
  1957. X    ** than the child that faked us initially.  Free the fake reference-
  1958. X    ** chain and process our references as usual.
  1959. X    */
  1960. X    for( node = article->parent; node; node = node->parent ) {
  1961. X        unlink_child( node );
  1962. X        free_article( node );
  1963. X    }
  1964. X    article->parent = Nullart;        /* neaten up */
  1965. X    article->siblings = Nullart;
  1966. X    }
  1967. X  check_references:
  1968. X    if( !*references ) {    /* If no references but "Re:" in subject, */
  1969. X    if( found_Re ) {    /* search for a reference in any cited text */
  1970. X#ifndef SERVER
  1971. X        for( len = 4; len && fgets( buff, sizeof buff, fp_article ); len-- ) {
  1972. X        if( (cp = index( buff, '<' )) && (end = index( cp, ' ' )) ) {
  1973. X            if( end[-1] == ',' ) {
  1974. X            end--;
  1975. X            }
  1976. X            *end = '\0';
  1977. X            if( (end = index( cp, '>' )) == Nullch ) {
  1978. X            end = cp + strlen( cp ) - 1;
  1979. X            }
  1980. X            if( valid_message_id( cp, end ) ) {
  1981. X            strcpy( references+1, cp );
  1982. X            *references = ' ';
  1983. X            if( log_verbosity > 2 ) {
  1984. X                log_error( "Found cited-text reference: '%s' [%ld]\n",
  1985. X                references+1, (long)num );
  1986. X            }
  1987. X            break;
  1988. X            }
  1989. X        }
  1990. X        }
  1991. X#endif
  1992. X    } else {
  1993. X        article->flags |= ROOT_ARTICLE;
  1994. X    }
  1995. X    }
  1996. X    /* If we have references, process them from the right end one at a time
  1997. X    ** until we either run into somebody, or we run out of references.
  1998. X    */
  1999. X    if( *references ) {
  2000. X    last = article;
  2001. X    node = Nullart;
  2002. X    end = references + strlen( references ) - 1;
  2003. X    while( (cp = rindex( references, ' ' )) != Nullch ) {
  2004. X        *cp++ = '\0';
  2005. X        while( end >= cp && ((unsigned char)*end <= ' ' || *end == ',') ) {
  2006. X        end--;
  2007. X        }
  2008. X        end[1] = '\0';
  2009. X        /* Quit parsing references if this one is garbage. */
  2010. X        if( !valid_message_id( cp, end ) ) {
  2011. X        if( log_verbosity ) {
  2012. X            log_error( "Bad ref '%s' [%ld]\n", cp, (long)num );
  2013. X        }
  2014. X        break;
  2015. X        }
  2016. X        /* Dump all domains that end in '.', such as "..." & "1@DEL." */
  2017. X        if( end[-1] == '.' ) {
  2018. X        break;
  2019. X        }
  2020. X        node = get_article( cp );
  2021. X        /* Check for duplicates on the reference line.  Brand-new data has
  2022. X        ** no date.  Data we just allocated earlier on this line has a
  2023. X        ** date but no root.  Special-case the article itself, since it
  2024. X        ** MIGHT have a root.
  2025. X        */
  2026. X        if( (node->date && !node->root) || node == article ) {
  2027. X        if( log_verbosity ) {
  2028. X            log_error( "Reference line contains duplicates [%ld]\n",
  2029. X            (long)num );
  2030. X        }
  2031. X        if( (node = last) == article ) {
  2032. X            node = Nullart;
  2033. X        }
  2034. X        continue;
  2035. X        }
  2036. X        last->parent = node;
  2037. X        link_child( last );
  2038. X        if( node->root ) {
  2039. X        break;
  2040. X        }
  2041. X        node->date = date;
  2042. X        last = node;
  2043. X        end = cp-2;
  2044. X    }
  2045. X    if( !node ) {
  2046. X        *references = '\0';
  2047. X        goto check_references;
  2048. X    }
  2049. X    /* Check if we ran into anybody that was already linked.  If so, we
  2050. X    ** just use their root.
  2051. X    */
  2052. X    if( node->root ) {
  2053. X        /* See if this article spans the gap between what we thought
  2054. X        ** were two different roots.
  2055. X        */
  2056. X        if( article->root && article->root != node->root ) {
  2057. X        merge_roots( node->root, article->root );
  2058. X        /* Set the roots of any children we brought with us. */
  2059. X        set_root( article, node->root );
  2060. X        }
  2061. X        use_root( article, node->root );
  2062. X    } else {
  2063. X        /* We didn't find anybody we knew, so either create a new root or
  2064. X        ** use the article's root if it was previously faked.
  2065. X        */
  2066. X        if( !article->root ) {
  2067. X        make_root( node );
  2068. X        use_root( article, node->root );
  2069. X        } else {
  2070. X        use_root( article, article->root );
  2071. X        node->root = article->root;
  2072. X        link_child( node );
  2073. X        }
  2074. X    }
  2075. X    /* Set the roots of the faked articles we created as references. */
  2076. X    for( node = article->parent; node && !node->root; node = node->parent ) {
  2077. X        node->root = article->root;
  2078. X    }
  2079. X    /* Make sure we didn't circularly link to a child article(!), by
  2080. X    ** ensuring that we run into the root before we run into ourself.
  2081. X    */
  2082. X    while( node && node->parent != article ) {
  2083. X        node = node->parent;
  2084. X    }
  2085. X    if( node ) {
  2086. X        /* Ugh.  Someone's tweaked reference line with an incorrect
  2087. X        ** article order arrived first, and one of our children is
  2088. X        ** really one of our ancestors. Cut off the bogus child branch
  2089. X        ** right where we are and link it to the root.
  2090. X        */
  2091. X        if( log_verbosity ) {
  2092. X        log_error("Found ancestral child -- fixing.\n");
  2093. X        }
  2094. X        unlink_child( node );
  2095. X        node->parent = Nullart;
  2096. X        link_child( node );
  2097. X    }
  2098. X    } else {
  2099. X    /* The article has no references.  Either turn it into a new root, or
  2100. X    ** re-attach fleshed-out (previously faked) article to its old root.
  2101. X    */
  2102. X    if( !article->root ) {
  2103. X        make_root( article );
  2104. X    } else {
  2105. X        use_root( article, article->root );
  2106. X        link_child( article );
  2107. X    }
  2108. X    }
  2109. X}
  2110. X
  2111. X/* Check if the string we've found looks like a valid message-id reference.
  2112. X*/
  2113. Xint
  2114. Xvalid_message_id( start, end )
  2115. Xregister char *start, *end;
  2116. X{
  2117. X    char *mid;
  2118. X
  2119. X    if( *end != '>' ) {
  2120. X    /* Compensate for spacecadets who include the header in their
  2121. X    ** subsitution of all '>'s into another citation character.
  2122. X    */
  2123. X    if( *end == '<' || *end == '-' || *end == '!' || *end == '%'
  2124. X     || *end == ')' || *end == '|' || *end == ':' || *end == '}'
  2125. X     || *end == '*' || *end == '+' || *end == '#' || *end == ']'
  2126. X     || *end == '@' ) {
  2127. X        if( log_verbosity ) {
  2128. X        log_error( "Reference ended in '%c'.\n", *end );
  2129. X        }
  2130. X        *end = '>';
  2131. X    }
  2132. X    }
  2133. X    /* Id must be "<...@...>" */
  2134. X    if( *start != '<' || *end != '>' || (mid = index( start, '@' )) == Nullch
  2135. X     || mid == start+1 || mid+1 == end ) {
  2136. X    return 0;                    /* RETURN */
  2137. X    }
  2138. X    return 1;
  2139. X}
  2140. X
  2141. X/* Remove an article from its parent/siblings.  Leave parent pointer intact.
  2142. X*/
  2143. Xvoid
  2144. Xunlink_child( child )
  2145. Xregister ARTICLE *child;
  2146. X{
  2147. X    register ARTICLE *last;
  2148. X
  2149. X    if( !(last = child->parent) ) {
  2150. X    child->root->thread_cnt--;
  2151. X    if( (last = child->root->articles) == child ) {
  2152. X        child->root->articles = child->siblings;
  2153. X    } else {
  2154. X        goto sibling_search;
  2155. X    }
  2156. X    } else {
  2157. X    last->child_cnt--;
  2158. X    if( last->children == child ) {
  2159. X        last->children = child->siblings;
  2160. X    } else {
  2161. X        last = last->children;
  2162. X      sibling_search:
  2163. X        while( last->siblings != child ) {
  2164. X        last = last->siblings;
  2165. X        }
  2166. X        last->siblings = child->siblings;
  2167. X    }
  2168. X    }
  2169. X}
  2170. X
  2171. X/* Link an article to its parent article.  If its parent pointer is zero,
  2172. X** link it to its root.  Sorts siblings by date.
  2173. X*/
  2174. Xvoid
  2175. Xlink_child( child )
  2176. Xregister ARTICLE *child;
  2177. X{
  2178. X    register ARTICLE *node;
  2179. X    register ROOT *root;
  2180. X
  2181. X    if( !(node = child->parent) ) {
  2182. X    root = child->root;
  2183. X    root->thread_cnt++;
  2184. X    node = root->articles;
  2185. X    if( !node || child->date < node->date ) {
  2186. X        child->siblings = node;
  2187. X        root->articles = child;
  2188. X    } else {
  2189. X        goto sibling_search;
  2190. X    }
  2191. X    } else {
  2192. X    node->child_cnt++;
  2193. X    node = node->children;
  2194. X    if( !node || child->date < node->date ) {
  2195. X        child->siblings = node;
  2196. X        child->parent->children = child;
  2197. X    } else {
  2198. X      sibling_search:
  2199. X        for( ; node->siblings; node = node->siblings ) {
  2200. X        if( node->siblings->date > child->date ) {
  2201. X            break;
  2202. X        }
  2203. X        }
  2204. X        child->siblings = node->siblings;
  2205. X        node->siblings = child;
  2206. X    }
  2207. X    }
  2208. X}
  2209. X
  2210. X/* Create a new root for the specified article.  If the current subject_str
  2211. X** matches any pre-existing root's subjects, we'll instead add it on as a
  2212. X** parallel thread.
  2213. X*/
  2214. Xvoid
  2215. Xmake_root( article )
  2216. XARTICLE *article;
  2217. X{
  2218. X    register ROOT *new, *node;
  2219. X    register SUBJECT *subject;
  2220. X
  2221. X#ifndef NO_SUBJECT_MATCHING
  2222. X    /* First, check the other root's subjects for a match. */
  2223. X    for( node = root_root; node; node = node->link ) {
  2224. X    for( subject = node->subjects; subject; subject = subject->link ) {
  2225. X        if( subject_equal( subject->str, subject_str ) ) {
  2226. X        use_root( article, node );        /* use it instead */
  2227. X        link_child( article );
  2228. X        return;                    /* RETURN */
  2229. X        }
  2230. X    }
  2231. X    }
  2232. X#endif
  2233. X
  2234. X    /* Create a new root. */
  2235. X    new = (ROOT *)safemalloc( sizeof (ROOT) );
  2236. X    total.root++;
  2237. X    new->articles = article;
  2238. X    new->root_num = article->num;
  2239. X    new->thread_cnt = 1;
  2240. X    if( article->num ) {
  2241. X    article->author = new_author();
  2242. X    new->subject_cnt = 1;
  2243. X    new->subjects = article->subject = new_subject();
  2244. X    } else {
  2245. X    new->subject_cnt = 0;
  2246. X    new->subjects = Null(SUBJECT*);
  2247. X    }
  2248. X    article->root = new;
  2249. X    new->link = root_root;
  2250. X    root_root = new;
  2251. X}
  2252. X
  2253. X/* Add this article's subject onto the indicated root's list.  Point the
  2254. X** article at the root.
  2255. X*/
  2256. Xvoid
  2257. Xuse_root( article, root )
  2258. XARTICLE *article;
  2259. XROOT *root;
  2260. X{
  2261. X    register SUBJECT *subject;
  2262. X    register ROOT *root2;
  2263. X    SUBJECT *hold, *child_subj = Null(SUBJECT*);
  2264. X    ARTICLE *node;
  2265. X
  2266. X    article->root = root;
  2267. X
  2268. X    /* If it's a fake, there's no subject to add. */
  2269. X    if( !article->num ) {
  2270. X    return;                        /* RETURN */
  2271. X    }
  2272. X
  2273. X    /* If we haven't picked a unique message number to represent this root,
  2274. X    ** use the first non-zero number we encounter.  Which one doesn't matter.
  2275. X    */
  2276. X    if( !root->root_num ) {
  2277. X    root->root_num = article->num;
  2278. X    }
  2279. X    article->author = new_author();
  2280. X
  2281. X    /* Check if the new subject matches any of the other subjects in this root.
  2282. X    ** If so, we just update the count.  If not, check all the other roots for
  2283. X    ** a match.  If found, the new subject is common between the two roots, so
  2284. X    ** we merge the two roots together.
  2285. X    */
  2286. X    root2 = root;
  2287. X#ifndef NO_SUBJECT_MATCHING
  2288. X    do {
  2289. X#endif
  2290. X    for( subject = root2->subjects; subject; subject = subject->link ) {
  2291. X        if( subject_equal( subject->str, subject_str ) ) {
  2292. X        article->subject = subject;
  2293. X        subject->count++;
  2294. X#ifndef NO_SUBJECT_MATCHING
  2295. X        if( root2 != root ) {
  2296. X            merge_roots( root, root2 );
  2297. X        }
  2298. X#endif
  2299. X        return;                    /* RETURN */
  2300. X        }
  2301. X    }
  2302. X#ifndef NO_SUBJECT_MATCHING
  2303. X    if( (root2 = root2->link) == Null(ROOT*) ) {
  2304. X        root2 = root_root;
  2305. X    }
  2306. X    } while( root2 != root );
  2307. X#endif
  2308. X
  2309. X    article->subject = hold = new_subject();
  2310. X    root->subject_cnt++;
  2311. X
  2312. X    /* Find subject of any pre-existing children.  We want to insert the new
  2313. X    ** subject before a child's to keep the subject numbering intuitive
  2314. X    ** in the newsreader.
  2315. X    */
  2316. X    for( node = article->children; node; node = node->children ) {
  2317. X    if( node->subject ) {
  2318. X        child_subj = node->subject;
  2319. X        break;
  2320. X    }
  2321. X    }
  2322. X    if( !(subject = root->subjects) || subject == child_subj ) {
  2323. X    hold->link = root->subjects;
  2324. X    root->subjects = hold;
  2325. X    } else {
  2326. X    while( subject->link && subject->link != child_subj ) {
  2327. X        subject = subject->link;
  2328. X    }
  2329. X    hold->link = subject->link;
  2330. X    subject->link = hold;
  2331. X    }
  2332. X}
  2333. X
  2334. X/* Check subjects in a case-insignificant, punctuation ignoring manner.
  2335. X*/
  2336. Xint
  2337. Xsubject_equal( str1, str2 )
  2338. Xregister char *str1, *str2;
  2339. X{
  2340. X    register char ch1, ch2;
  2341. X
  2342. X    while( (ch1 = *str1++) ) {
  2343. X    if( ch1 == ' ' || ispunct( ch1 ) ) {
  2344. X        while( *str1 && (*str1 == ' ' || ispunct( *str1 )) ) {
  2345. X        str1++;
  2346. X        }
  2347. X        ch1 = ' ';
  2348. X    } else if( isupper( ch1 ) ) {
  2349. X        ch1 = tolower( ch1 );
  2350. X    }
  2351. X    if( !(ch2 = *str2++) ) {
  2352. X        return 0;
  2353. X    }
  2354. X    if( ch2 == ' ' || ispunct( ch2 ) ) {
  2355. X        while( *str2 && (*str2 == ' ' || ispunct( *str2 )) ) {
  2356. X        str2++;
  2357. X        }
  2358. X        ch2 = ' ';
  2359. X    } else if( isupper( ch2 ) ) {
  2360. X        ch2 = tolower( ch2 );
  2361. X    }
  2362. X    if( ch1 != ch2 ) {
  2363. X        return 0;
  2364. X    }
  2365. X    }
  2366. X    if( *str2 ) {
  2367. X    return 0;
  2368. X    }
  2369. X    return 1;
  2370. X}
  2371. X
  2372. X/* Create a new subject structure. */
  2373. XSUBJECT *
  2374. Xnew_subject()
  2375. X{
  2376. X    register int len = strlen( subject_str ) + 1;
  2377. X    register SUBJECT *subject;
  2378. X
  2379. X    subject = (SUBJECT *)safemalloc( sizeof (SUBJECT) );
  2380. X    total.subject++;
  2381. X    subject->count = 1;
  2382. X    subject->link = Null(SUBJECT*);
  2383. X    subject->str = safemalloc( len );
  2384. X    total.string1 += len;
  2385. X    bcopy( subject_str, subject->str, len );
  2386. X
  2387. X    return subject;
  2388. X}
  2389. X
  2390. X/* Create a new author structure. */
  2391. XAUTHOR *
  2392. Xnew_author()
  2393. X{
  2394. X    register len = strlen( author_str ) + 1;
  2395. X    register AUTHOR *author, *last_author;
  2396. X
  2397. X    last_author = Null(AUTHOR*);
  2398. X    for( author = author_root; author; author = author->link ) {
  2399. X#ifndef DONT_COMPARE_AUTHORS    /* might like to define this to save time */
  2400. X    if( strEQ( author->name, author_str ) ) {
  2401. X        author->count++;
  2402. X        return author;                /* RETURN */
  2403. X    }
  2404. X#endif
  2405. X    last_author = author;
  2406. X    }
  2407. X
  2408. X    author = (AUTHOR *)safemalloc( sizeof (AUTHOR) );
  2409. X    total.author++;
  2410. X    author->count = 1;
  2411. X    author->link = Null(AUTHOR*);
  2412. X    author->name = safemalloc( len );
  2413. X    total.string1 += len;
  2414. X    bcopy( author_str, author->name, len );
  2415. X
  2416. X    if( last_author ) {
  2417. X    last_author->link = author;
  2418. X    } else {
  2419. X    author_root = author;
  2420. X    }
  2421. X    return author;
  2422. X}
  2423. X
  2424. X/* Insert all of root2 into root1, setting the proper root values and
  2425. X** updating subject counts.
  2426. X*/
  2427. Xvoid
  2428. Xmerge_roots( root1, root2 )
  2429. XROOT *root1, *root2;
  2430. X{
  2431. X    register ARTICLE *node, *next;
  2432. X    register SUBJECT *subject;
  2433. X
  2434. X    /* Remember whoever's root num is lower.  This could screw up a
  2435. X    ** newsreader's kill-thread code if someone already saw the roots as
  2436. X    ** being separate, but it must be done.  The newsreader code will have
  2437. X    ** to handle this as best as it can.
  2438. X    */
  2439. X    if( root1->root_num > root2->root_num ) {
  2440. X    root1->root_num = root2->root_num;
  2441. X    }
  2442. X
  2443. X    for( node = root2->articles; node; node = next ) {
  2444. X    /* For each article attached to root2, detach them, set the
  2445. X    ** branch's root pointers to root1, and then attach it to root1.
  2446. X    */
  2447. X    next = node->siblings;
  2448. X    unlink_child( node );
  2449. X    node->siblings = Nullart;
  2450. X    set_root( node, root1 );        /* sets children too */
  2451. X    /* Link_child() depends on node->parent being null and node->root
  2452. X    ** being set.
  2453. X    */
  2454. X    link_child( node );
  2455. X    }
  2456. X    root1->subject_cnt += root2->subject_cnt;
  2457. X    if( !(subject = root1->subjects) ) {
  2458. X    root1->subjects = root2->subjects;
  2459. X    } else {
  2460. X    while( subject->link ) {
  2461. X        subject = subject->link;
  2462. X    }
  2463. X    subject->link = root2->subjects;
  2464. X    }
  2465. X    unlink_root( root2 );
  2466. X    free_root( root2 );
  2467. X}
  2468. X
  2469. X/* When merging roots, we need to reset all the root pointers.
  2470. X*/
  2471. Xvoid
  2472. Xset_root( node, root )
  2473. XARTICLE *node;
  2474. XROOT *root;
  2475. X{
  2476. X    do {
  2477. X    node->root = root;
  2478. X    if( node->children ) {
  2479. X        set_root( node->children, root );
  2480. X    }
  2481. X    } while( node = node->siblings );
  2482. X}
  2483. X
  2484. X/* Unlink a root from its neighbors. */
  2485. Xvoid
  2486. Xunlink_root( root )
  2487. Xregister ROOT *root;
  2488. X{
  2489. X    register ROOT *node;
  2490. X
  2491. X    if( (node = root_root) == root ) {
  2492. X    root_root = root->link;
  2493. X    } else {
  2494. X    while( node->link != root ) {
  2495. X        node = node->link;
  2496. X    }
  2497. X    node->link = root->link;
  2498. X    }
  2499. X}
  2500. X
  2501. X/* Free an article and its message-id string.  All other resources must
  2502. X** already be free, and it must not be attached to any threads.
  2503. X*/
  2504. Xvoid
  2505. Xfree_article( this )
  2506. XARTICLE *this;
  2507. X{
  2508. X    register ARTICLE *art;
  2509. X
  2510. X    if( (art = this->domain->ids) == this ) {
  2511. X    if( !(this->domain->ids = this->id_link) ) {
  2512. X        free_domain( this->domain );
  2513. X    }
  2514. X    } else {
  2515. X    while( this != art->id_link ) {
  2516. X        art = art->id_link;
  2517. X    }
  2518. X    art->id_link = this->id_link;
  2519. X    }
  2520. X    total.string2 -= strlen( this->id ) + 1;
  2521. X    free( this->id );
  2522. X    free( this );
  2523. X    total.article--;
  2524. X}
  2525. X
  2526. X/* Free the domain only when its last unique id has been freed. */
  2527. Xvoid
  2528. Xfree_domain( this )
  2529. XDOMAIN *this;
  2530. X{
  2531. X    register DOMAIN *domain;
  2532. X
  2533. X    if( this == (domain = &unk_domain) ) {
  2534. X    return;
  2535. X    }
  2536. X    if( this == next_domain ) {    /* help expire routine skip freed domains */
  2537. X    next_domain = next_domain->link;
  2538. X    }
  2539. X    while( this != domain->link ) {
  2540. X    domain = domain->link;
  2541. X    }
  2542. X    domain->link = this->link;
  2543. X    total.string2 -= strlen( this->name ) + 1;
  2544. X    free( this->name );
  2545. X    free( this );
  2546. X    total.domain--;
  2547. X}
  2548. X
  2549. X/* Free the subject structure and its string. */
  2550. Xvoid
  2551. Xfree_subject( this )
  2552. XSUBJECT *this;
  2553. X{
  2554. X    total.string1 -= strlen( this->str ) + 1;
  2555. X    free( this->str );
  2556. X    free( this );
  2557. X    total.subject--;
  2558. X}
  2559. X
  2560. X/* Free a root.  It must already be unlinked. */
  2561. Xvoid
  2562. Xfree_root( this )
  2563. XROOT *this;
  2564. X{
  2565. X    free( this );
  2566. X    total.root--;
  2567. X}
  2568. X
  2569. X/* Free the author structure when it's not needed any more. */
  2570. Xvoid
  2571. Xfree_author( this )
  2572. XAUTHOR *this;
  2573. X{
  2574. X    total.string1 -= strlen( this->name ) + 1;
  2575. X    free( this->name );
  2576. X    free( this );
  2577. X    total.author--;
  2578. X}
  2579. END_OF_FILE
  2580.   if test 34306 -ne `wc -c <'mt-process.c'`; then
  2581.     echo shar: \"'mt-process.c'\" unpacked with wrong size!
  2582.   fi
  2583.   # end of 'mt-process.c'
  2584. fi
  2585. echo shar: End of archive 5 \(of 14\).
  2586. cp /dev/null ark5isdone
  2587. MISSING=""
  2588. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  2589.     if test ! -f ark${I}isdone ; then
  2590.     MISSING="${MISSING} ${I}"
  2591.     fi
  2592. done
  2593. if test "${MISSING}" = "" ; then
  2594.     echo You have unpacked all 14 archives.
  2595.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2596. else
  2597.     echo You still must unpack the following archives:
  2598.     echo "        " ${MISSING}
  2599. fi
  2600. exit 0
  2601.